package com.ap.core.email.impl;

import com.ap.core.email.exception.InvalidEmailAddressException;
import com.ap.core.email.exception.JavaMailConfigException;
import com.ap.core.email.exception.SMTPUnknownHostException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.mail.*;
import javax.mail.Message.RecipientType;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;


/**
 * This implementation of JavaMail to send email
 * 
 */
class JavaMailImpl {

    private Log logger = LogFactory.getLog(JavaMailImpl.class);

    private Session mailSession;

    private boolean parseEmailAddressToCheckRFC822;

     private String charset;

    /**
     * Sets up this session.
     * 
     * @param host URL
     * @param protocol SMTP
     * @param parseEmailAddressToCheckRFC822 whether to apply email address check
     * @param charSet to send the email with
     */
    public JavaMailImpl(String host, String protocol, boolean parseEmailAddressToCheckRFC822,
                        String charSet) {
        this.parseEmailAddressToCheckRFC822 = parseEmailAddressToCheckRFC822;
        this.charset = charSet;
        Properties props = new Properties();
        props.put("mail.transport.protocol", protocol);
        props.put("mail.smtp.host", host);
        Authenticator auth = new ApSmtpAuthenticator();
        mailSession = Session.getInstance(props, auth);

    }

    public  static void main(String[] args){

        JavaMailImpl javaMail = new JavaMailImpl("localhost:25", "SMTP", false, "UTF-8");
        javaMail.sendMail("Hello", "Hi There", "ranjit.singh@vocalink.com", new  String[]{"ranjit.singh@vocalink.com"}, null, null, false);
    }
    /**
     * Only method in class.
     * 
     * @param mailSubject email subject line
     * @param mailBody content
     * @param mailFrom From address
     * @param mailTo TO addresses (RFC822 syntax)
     * @param mailCC CC addresses (RFC822 syntax)
     * @param mailBCC BCC addresses (RFC822 syntax)
     * @param highPriority flag if needed
     */
    void sendMail(String mailSubject, String mailBody,
                  InternetAddress mailFrom, InternetAddress[] mailTo,
                  InternetAddress[] mailCC, InternetAddress[] mailBCC,
                  boolean highPriority) {

        RuntimeException sendEmailException = null;
        try {
            // Construct a MimeMessage
            Message message = new MimeMessage(mailSession);
            message.setFrom(mailFrom);
            message.setRecipients(RecipientType.TO, mailTo);

            if (highPriority) {
                message.setHeader("Priority", "urgent");
                message.setHeader("X-Priority", "1 (Highest)");
                message.setHeader("X-MSMail-Priority", "High");
            }

            if (mailCC != null) {
                message.setRecipients(RecipientType.CC, mailCC);
            }
            if (mailBCC != null) {
                message.setRecipients(RecipientType.BCC, mailBCC);
            }

            // set subject/body
            message.setSubject(mailSubject);
            message.setSentDate(new Date());
            message.setText(mailBody);

            // Send the message.
            Transport.send(message);
        } catch (SendFailedException sfe) {
            sendEmailException = new JavaMailConfigException(sfe.getMessage(), sfe);
            Exception e = sfe.getNextException();
            if (e instanceof MessagingException) {
                MessagingException me = (MessagingException) e;
                Exception e2 = me.getNextException();
                if (e2 instanceof UnknownHostException) {
                    sendEmailException = new SMTPUnknownHostException(e2
                            .getMessage(), e2);
                }
            }
        } catch (MessagingException me) {
            sendEmailException = new JavaMailConfigException(me.getMessage(), me);
        } finally {
            if (sendEmailException != null) {
                logger.error(sendEmailException.getMessage(), sendEmailException);
                throw sendEmailException;
            }
        }
    }


    /**
     * Convenience overload method. Converts String arrays of email
     * addresses before calling main method.
     * 
     * @param mailSubject text
     * @param mailBody text
     * @param mailFrom sender
     * @param mailTo recipient
     * @param mailCC one email per array element
     * @param mailBCC one email per array element
     * @param highPriority flag if needed
     */
    void sendMail(String mailSubject, String mailBody, String mailFrom,
                  String[] mailTo, String[] mailCC, String[] mailBCC,
                  boolean highPriority) {

        InternetAddress iaFrom = singleIA(mailFrom);
        InternetAddress[] iaTos = stringToIA(mailTo);
        InternetAddress[] iaCCs = stringToIA(mailCC);
        InternetAddress[] iaBCCs = stringToIA(mailBCC);
        this.sendMail(
            mailSubject,
            mailBody,
            iaFrom,
            iaTos,
            iaCCs,
            iaBCCs,
            highPriority);

    }

    /**
     * Convenience overload method, without priority flag.
     * 
     * @see com.ap.core.email.impl.JavaMailImpl
     * @param mailSubject email subject line
     * @param mailBody content
     * @param mailFrom From address
     * @param mailTo TO addresses (RFC822 syntax)
     * @param mailCC CC addresses (RFC822 syntax)
     * @param mailBCC BCC addresses (RFC822 syntax)
     */
    void sendMail(String mailSubject, String mailBody,
                  InternetAddress mailFrom, InternetAddress[] mailTo,
                  InternetAddress[] mailCC, InternetAddress[] mailBCC) {
        this.sendMail(
            mailSubject,
            mailBody,
            mailFrom,
            mailTo,
            mailCC,
            mailBCC,
            false);
    }



    /**
     * Creates a single InternetAddress.
     * 
     * @param emailAddress plain string
     * @return a internet address object
     */
    InternetAddress singleIA(String emailAddress) {

        InternetAddress ia;
        try {
            // test to see if it passes RFC822
            if (this.parseEmailAddressToCheckRFC822) {
                InternetAddress.parse(emailAddress, true);
            }

            if (this.charset != null) {
                ia = new InternetAddress(emailAddress, null, this.charset);
            } else {
                ia = new InternetAddress(emailAddress);
            }
        } catch (UnsupportedEncodingException e) {
            throw new InvalidEmailAddressException(e.getMessage(), e);
        } catch (AddressException e) {
            throw new InvalidEmailAddressException(e.getMessage(), e);
        }
        return ia;
    }

    /**
     * Converts an array of strings into InternetAddresses.
     * 
     * @param stringAdds the array
     * @return array of IAs
     */
    private InternetAddress[] stringToIA(String[] stringAdds) {

        InternetAddress[] ias;
        String stringAdd;
        if (stringAdds == null) {
            ias = null;
        } else {

            int len = stringAdds.length;
            ArrayList list = new ArrayList();

            for (int x = 0; x < len; x++) {
                stringAdd = stringAdds[x];
                if (stringAdd != null && stringAdd.trim().length() > 0) {
                    list.add(singleIA(stringAdd));
                }
            }

            InternetAddress[] iaType = new InternetAddress[0];
            ias = (InternetAddress[]) list.toArray(iaType);
        }
        return ias;
    }
}
